package net.sudot.chess.plugin;

import net.sudot.chess.business.model.PluginConfig;
import net.sudot.chess.business.service.PluginConfigService;
import net.sudot.chess.config.SystemProperties;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 基础插件
 *
 * @author tangjialin on 2018-03-18.
 */
public abstract class BasePlugin implements InitializingBean, Comparable<BasePlugin> {
    /** 插件缓存有效期.仅用于降低连续获取配置时数据库压力,有效期不应过长 */
    private static final long CACHE_TIME_MILLIS = 1000L * 60;
    @Resource
    protected SystemProperties systemProperties;
    @Resource
    private PluginConfigService pluginConfigService;
    /** 插件配置过期时间 */
    private volatile long expireTime = 0L;
    /** 插件配置 */
    private volatile PluginConfig pluginConfig;
    /** 插件的标识信息(插件的实现类在spring容器中的名称) */
    private String id;

    @Override
    public void afterPropertiesSet() {
        Component component = AnnotatedElementUtils.findMergedAnnotation(getClass(), Component.class);
        String value = component.value();
        if (value.isEmpty()) {
            // 类名首字母小写
            value = StringUtils.uncapitalize(getClass().getSimpleName());
        }
        id = value;
    }

    /**
     * 清除缓存
     */
    public void clearCache() {
        expireTime = 0L;
    }

    /**
     * 获取ID
     *
     * @return ID
     */
    public String getId() {
        return id;
    }

    /**
     * 获取插件配置
     *
     * @return 插件配置
     */
    public PluginConfig getConfig() {
        long currentTimeMillis = System.currentTimeMillis();
        if (expireTime < currentTimeMillis) {
            /** 缓存插件配置,降低连续获取配置时数据库压力.无需同步,即使重复加载也没有关系 */
            pluginConfig = pluginConfigService.findByPluginId(id);
            expireTime = currentTimeMillis + CACHE_TIME_MILLIS;
        }
        return pluginConfig;
    }

    /**
     * 获取名称
     *
     * @return 名称
     */
    public abstract String getName();

    /**
     * 获取插件显示的名称
     *
     * @return 插件名称
     */
    public String getDisplayName() {
        PluginConfig pluginConfig = getConfig();
        String displayName = pluginConfig == null ? getName() : pluginConfig.getDisplayName();
        return displayName == null || displayName.isEmpty() ? getName() : displayName;
    }

    /**
     * 获取扩展属性配置
     *
     * @return 属性配置
     */
    public abstract List<PluginAttributeConfig> getAttributeConfigs();

    /**
     * 获取是否已安装
     *
     * @return 是否已安装
     */
    public boolean getIsInstalled() {
        return getConfig() != null;
    }

    /**
     * 获取是否已启用
     *
     * @return 是否已启用
     */
    public boolean getIsEnabled() {
        PluginConfig pluginConfig = getConfig();
        return pluginConfig != null && Boolean.TRUE.equals(pluginConfig.getEnabled());
    }

    /**
     * 获取属性值
     *
     * @param name 属性名称
     * @return 属性值
     */
    public String getAttribute(String name) {
        PluginConfig pluginConfig = getConfig();
        return pluginConfig != null ? pluginConfig.getAttribute(name) : null;
    }

    /**
     * 获取排序
     *
     * @return 排序
     */
    public Integer getOrder() {
        PluginConfig pluginConfig = getConfig();
        return pluginConfig != null && pluginConfig.getOrder() != null ? pluginConfig.getOrder() : Integer.MAX_VALUE;
    }

    /**
     * 是否支持
     *
     * @param request HttpServletRequest
     * @return 是否支持
     */
    public boolean supports(HttpServletRequest request) {
        return true;
    }

    /**
     * 连接Map键值对
     *
     * @param map              Map
     * @param prefix           前缀
     * @param suffix           后缀
     * @param separator        连接符
     * @param ignoreEmptyValue 忽略空值
     * @param ignoreKeys       忽略Key
     * @return 字符串
     */
    protected String joinKeyValue(Map<String, Object> map, String prefix, String suffix, String separator, boolean ignoreEmptyValue, String... ignoreKeys) {
        List<String> list = new ArrayList<>();
        if (map != null) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                String value = String.valueOf(entry.getValue());
                if (StringUtils.isNotEmpty(key) && !ArrayUtils.contains(ignoreKeys, key) && (!ignoreEmptyValue || StringUtils.isNotEmpty(value))) {
                    list.add(key + "=" + (value != null ? value : ""));
                }
            }
        }
        return (prefix != null ? prefix : "") + StringUtils.join(list, separator) + (suffix != null ? suffix : "");
    }

    /**
     * 连接Map值
     *
     * @param map              Map
     * @param prefix           前缀
     * @param suffix           后缀
     * @param separator        连接符
     * @param ignoreEmptyValue 忽略空值
     * @param ignoreKeys       忽略Key
     * @return 字符串
     */
    protected String joinValue(Map<String, Object> map, String prefix, String suffix, String separator, boolean ignoreEmptyValue, String... ignoreKeys) {
        List<String> list = new ArrayList<>();
        if (map != null) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                String value = String.valueOf(entry.getValue());
                if (StringUtils.isNotEmpty(key) && !ArrayUtils.contains(ignoreKeys, key) && (!ignoreEmptyValue || StringUtils.isNotEmpty(value))) {
                    list.add(value != null ? value : "");
                }
            }
        }
        return (prefix != null ? prefix : "") + StringUtils.join(list, separator) + (suffix != null ? suffix : "");
    }

    /**
     * 实现compareTo方法
     *
     * @param plugin 插件
     * @return 比较结果
     */
    @Override
    public int compareTo(BasePlugin plugin) {
        if (plugin == null) { return -1; }
        boolean otherNotInstalled = !plugin.getIsInstalled();
        if (otherNotInstalled && !this.getIsInstalled()) { return 0; }
        if (otherNotInstalled) { return -1; }
        return getOrder().compareTo(plugin.getOrder());
    }

    /**
     * 重写equals方法
     *
     * @param obj 对象
     * @return 是否相等
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (this == obj) {
            return true;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        BasePlugin other = (BasePlugin) obj;
        return getId().equals(other.getId());
    }

    /**
     * 重写hashCode方法
     *
     * @return HashCode
     */
    @Override
    public int hashCode() {
        return super.hashCode();
    }

}